package com.cygsunri.solar.task;

import com.cygsunri.common.constant.DataName;
import com.cygsunri.common.constant.DeviceType;
import com.cygsunri.common.service.CommonDataService;
import com.cygsunri.information.entity.Information;
import com.cygsunri.information.service.InformationService;
import com.cygsunri.measurement.calculate.AbstractComplexEndureCalculateService;
import com.cygsunri.measurement.entity.MeasurementValue;
import com.cygsunri.scada.base.BasePSR;
import com.cygsunri.scada.service.ScadaMeasurementService;
import com.cygsunri.scada.service.ScadaPSRService;
import com.cygsunri.util.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 光伏子站计算量
 */
@Component
@Slf4j
@ConditionalOnProperty(prefix = "task.solar", name = "sub", havingValue = "true")
@PropertySource(name = "application.yml", value = {"classpath:application.yml"}, ignoreResourceNotFound = false, encoding = "UTF-8")
public class SolarSubCalcTask extends AbstractComplexEndureCalculateService {

    @Autowired
    private ScadaPSRService scadaPSRService;

    @Autowired
    private ScadaMeasurementService scadaMeasurementService;

    @Autowired
    private CommonDataService commonDataService;

    @Autowired
    private InformationService informationService;

    private Map<BasePSR, List<BasePSR>> map = new HashMap<>();
    private Map<BasePSR, List<BasePSR>> childrenMap = new HashMap<>();

    @Bean
    public void solarSubCalcInit() {
        List<BasePSR> subs = scadaPSRService.getDevicesByType(DeviceType.SOLAR_SUB.getKey());
        for (BasePSR sub : subs) {
            List<BasePSR> inverters = scadaPSRService.getDeviceOfSubstationByType(sub.getId(), DeviceType.INVERTER.getKey());
            if (inverters.isEmpty()) continue;
            map.put(sub, inverters);

            List<BasePSR> children = scadaPSRService.getDeviceOfSubstation(sub.getId());
            if (children.isEmpty()) continue;
            childrenMap.put(sub, children);
        }
    }

    @Scheduled(cron = "${task.solar.cron}")
    private void solarSubCalc() {
        long a = System.currentTimeMillis();

        map.forEach((sub, inverters) -> {
            calcPower(sub, inverters);
            calcGenerated(sub, inverters, DataName.DAY_GENERATED);
            calcGenerated(sub, inverters, DataName.MONTH_GENERATED);
            calcGenerated(sub, inverters, DataName.YEAR_GENERATED);
            calcGenerated(sub, inverters, DataName.TOTAL_GENERATED);
            calcEfficiency(sub);
            // calcInverterStatus(inverters);
        });

        childrenMap.forEach(this::solarDeviceStatus);

        log.info("光伏子站计算量耗时{}ms", System.currentTimeMillis() - a);
    }

    /**
     * 光伏功率计算
     */
    private void calcPower(BasePSR sub, List<BasePSR> inverters) {
        if (!this.getNeedEndureCalculate(sub.getId(), sub.getName(), DataName.TOTAL_ACTIVE_POWER)) {
            return;
        }

        ImmutablePair<String, Integer> pair = scadaMeasurementService.getMeasurementID(sub.getId(), DataName.TOTAL_ACTIVE_POWER);
        if (pair == null) {
            log.warn("找不到id为{}的设备{}在scada中的{}点！", sub.getId(), sub.getName(), DataName.TOTAL_ACTIVE_POWER);
            return;
        }

        Double value = 0d;
        Integer num = 0;
        for (BasePSR inverter : inverters) {
            MeasurementValue outPower = commonDataService.getValue(inverter.getId(), DataName.OUT_POWER);
            if (!outPower.isInValid() && outPower.getData() >= 0) {
                value += outPower.getData();
                num++;
            }
        }

        if (num > 0) {
            this.saveMean(pair.getLeft(), value, DateUtil.nowMilliSeconds(), MeasurementValue.Quality.Calc.getKey());
        }
    }

    /**
     * 光伏发电量计算
     */
    private void calcGenerated(BasePSR sub, List<BasePSR> inverters, String dataName) {
        if (!this.getNeedEndureCalculate(sub.getId(), sub.getName(), dataName)) {
            return;
        }

        ImmutablePair<String, Integer> pair = scadaMeasurementService.getMeasurementID(sub.getId(), dataName);
        if (pair == null) {
            log.warn("找不到id为{}的设备{}在scada中的{}点！", sub.getId(), sub.getName(), dataName);
            return;
        }

        ImmutablePair<String, Integer> inverterPair = scadaMeasurementService.getMeasurementID(inverters.get(0).getId(), DataName.TOTAL_GENERATED);
        if (inverterPair == null) {
            return;
        }

        ImmutablePair<Double, Integer> valuePair;
        if (dataName.equals(DataName.TOTAL_GENERATED)) {
            valuePair = calcFromInverter(inverters, dataName);
        } else {
            valuePair = calcFromInverterDiff(inverters, dataName);
        }


        if (valuePair != null && valuePair.getRight() > 0) {
            this.saveMean(pair.getLeft(), valuePair.getLeft(), DateUtil.nowMilliSeconds(), MeasurementValue.Quality.Calc.getKey());
        }
    }

    /**
     * 逆变器累加
     */
    private ImmutablePair<Double, Integer> calcFromInverter(List<BasePSR> inverters, String dataName) {
        Double value = 0d;
        int num = 0;

        for (BasePSR inverter : inverters) {
            MeasurementValue m = commonDataService.getValue(inverter.getId(), dataName);
            if (!m.isInValid() && m.getData() >= 0) {
                value += m.getData();
                num++;
            }
        }
        return new ImmutablePair<>(value, num);
    }

    /**
     * 逆变器差值累加
     */
    private ImmutablePair<Double, Integer> calcFromInverterDiff(List<BasePSR> inverters, String dataName) {
        Double value = 0d;
        int num = 0;

        Integer flag;
        switch (dataName) {
            case DataName.DAY_GENERATED:
                flag = 500;
                break;
            case DataName.MONTH_GENERATED:
                flag = 502;
                break;
            case DataName.YEAR_GENERATED:
                flag = 503;
                break;
            default:
                return null;
        }

        for (BasePSR inverter : inverters) {
            MeasurementValue m = commonDataService.getValue(inverter.getId(), DataName.TOTAL_GENERATED, DateUtil.nowMilliSeconds(), flag);
            if (!m.isInValid() && m.getData() >= 0) {
                value += m.getData();
                num++;
            }
        }
        return new ImmutablePair<>(value, num);
    }

    /**
     * 光伏效率
     */
    private void calcEfficiency(BasePSR sub) {
        if (!this.getNeedEndureCalculate(sub.getId(), sub.getName(), DataName.EFFICIENCY)) {
            return;
        }

        ImmutablePair<String, Integer> pair = scadaMeasurementService.getMeasurementID(sub.getId(), DataName.EFFICIENCY);
        if (pair == null) {
            log.warn("找不到id为{}的设备{}在scada中的{}点！", sub.getId(), sub.getName(), DataName.EFFICIENCY);
            return;
        }

        Information information = informationService.getInformationByPsrID(sub.getId());
        if (information == null || information.getCapacity() == null || information.getCapacity() == 0) {
            return;
        }

        List<BasePSR> weathers = scadaPSRService.getDeviceOfSubstationByType(sub.getId(), DeviceType.WEATHER_INC.getKey());
        if (!weathers.isEmpty()) {
            MeasurementValue radiation = commonDataService.getValue(weathers.get(0).getId(), DataName.ACCUMULATE_RADIATION);
            MeasurementValue dayGenerated = commonDataService.getValue(sub.getId(), DataName.DAY_GENERATED);

            if (radiation.isInValid() || radiation.getData() == 0 || dayGenerated.isInValid()) {
                return;
            }
            Double slope = radiation.getUnit().toLowerCase().contains("kwh") ? 1d : 3.6d;
            Double pr = slope * dayGenerated.getData() / radiation.getData() / information.getCapacity();
            if (pr > 1) return;
            this.saveMean(pair.getLeft(), pr * 100, DateUtil.nowMilliSeconds(), MeasurementValue.Quality.Calc.getKey());

        } else {
            MeasurementValue power = commonDataService.getValue(sub.getId(), DataName.TOTAL_ACTIVE_POWER);
            if (!power.isInValid()) {
                Double pr = power.getData() / information.getCapacity();
                if (pr > 1) return;
                this.saveMean(pair.getLeft(), pr * 100, DateUtil.nowMilliSeconds(), MeasurementValue.Quality.Calc.getKey());
            }
        }
    }

    /**
     * 逆变器状态
     */
    private void calcInverterStatus(BasePSR inverter) {
        MeasurementValue communicationFault = commonDataService.getValue(inverter.getId(), DataName.COMMUNICATION_FAULT);
        if (!communicationFault.isInValid() && communicationFault.getData() == 1) {
            setDeviceStatus(inverter, "COMMUNICATION_FAULT");
            return;
        }

        MeasurementValue inverterStatus = commonDataService.getValue(inverter.getId(), DataName.INVERTER_STATUS);
        if (inverterStatus.isInValid()) {
            setDeviceStatus(inverter, "UNKNOWN");
            return;
        }
        switch (inverterStatus.getData().intValue()) {
            case 0:
                setDeviceStatus(inverter, "STANDBY");
                break;
            case 1:
                setDeviceStatus(inverter, "RUNNING");
                break;
            case 2:
                setDeviceStatus(inverter, "RUNNING");
                break;
            case 3:
                setDeviceStatus(inverter, "RUNNING");
                break;
            case 4100:
                setDeviceStatus(inverter, "STOP");
                break;
            default:
                setDeviceStatus(inverter, "FAULT");
                break;
        }

//        MeasurementValue fault = commonDataService.getValue(inverter.getId(), DataName.FAULT);
//        if (!fault.isInValid() && fault.getData() == 1) {
//            setDeviceStatus(inverter, "FAULT");
//            return;
//        }
//
//        MeasurementValue stop = commonDataService.getValue(inverter.getId(), DataName.STOP);
//        if (!stop.isInValid() && stop.getData() == 1) {
//            setDeviceStatus(inverter, "STOP");
//            return;
//        }
//
//        MeasurementValue standby = commonDataService.getValue(inverter.getId(), DataName.STANDBY);
//        if (!standby.isInValid() && standby.getData() == 1) {
//            setDeviceStatus(inverter, "STANDBY");
//            return;
//        }
//
//        MeasurementValue running = commonDataService.getValue(inverter.getId(), DataName.RUNNING);
//        if (!running.isInValid() && running.getData() == 1) {
//            setDeviceStatus(inverter, "RUNNING");
//            return;
//        }


    }

    /**
     * 光伏设备通讯状态
     */
    private void solarDeviceStatus(BasePSR sub, List<BasePSR> children) {
        for (BasePSR basePSR : children) {
            if (basePSR.getPsrType().getType() == DeviceType.INVERTER.getKey()) {
                calcInverterStatus(basePSR);
            } else {
                MeasurementValue communicationFault = commonDataService.getValue(basePSR.getId(), DataName.COMMUNICATION_FAULT);
                if (!communicationFault.isInValid() && communicationFault.getData() == 1) {
                    setDeviceStatus(basePSR, "COMMUNICATION_FAULT");
                } else {
                    setDeviceStatus(basePSR, "RUNNING");
                }
            }
        }
    }
}
